package com.googlecode.animator.view.timeline
{
	import com.googlecode.animator.data.Notifications;
	import com.googlecode.data.markers.BaseMarker;
	import com.googlecode.data.markers.GlobalMarkerVO;
	import com.googlecode.data.markers.HideMarkerVO;
	import com.googlecode.data.markers.KeyframeVO;
	import com.googlecode.data.objects3d.Object3DVO;
	import com.googlecode.animator.model.ProjectSettingsProxy;
	
	import flash.events.Event;
	import flash.utils.Dictionary;
	
	import org.papervision3d.core.math.Number3D;
	import org.puremvc.as3.multicore.interfaces.INotification;
	import org.puremvc.as3.multicore.interfaces.IProxy;
	import org.puremvc.as3.multicore.patterns.mediator.Mediator;

	public class TimelineGridMediator extends Mediator
	{
		private var _autoKeyframing:Boolean;
		private var _currentTimeInterval:int;
		private static var _count:int;
		
		/**
		 * Identifier. 
		 */
		public static const NAME:String = "TimelineGridMediator";
		
		/**
		 * Constructor 
		 * @param viewComponent
		 * 
		 */
		public function TimelineGridMediator(viewComponent:TimelineGrid)
		{
			super(NAME, viewComponent);
			
			_autoKeyframing = true;

			_count = 0;
		}
		
		/**
		 * Triggered by PureMVC. 
		 * 
		 */
		override public function onRegister():void
		{
			super.onRegister();
			
			// add event listeners		
			viewRef.addEventListener(SnapDraggableObject.OBJECT_UP, onObjectUp);
			viewRef.addEventListener(SnapDraggableObject.OBJECT_MOVE, onObjectMove);
			viewRef.addEventListener(Marker.MARKER_DELETE, onMarkerDelete);
			viewRef.addEventListener(Marker.MARKER_DELETE_ALL, onMarkerDeleteAll);
			viewRef.addEventListener(Marker.MARKER_COPY, onMarkerCopy);
			viewRef.addEventListener(Marker.MARKER_PASTE, onMarkerPaste);
		}
		
		/**
		 * Clean up. 
		 * 
		 */
		override public function onRemove():void
		{
			super.onRemove();
			
			// reomove event listeners		
			viewRef.removeEventListener(SnapDraggableObject.OBJECT_UP, onObjectUp);
			viewRef.removeEventListener(SnapDraggableObject.OBJECT_MOVE, onObjectMove);
			viewRef.removeEventListener(Marker.MARKER_DELETE, onMarkerDelete);
			viewRef.removeEventListener(Marker.MARKER_DELETE_ALL, onMarkerDeleteAll);
			viewRef.removeEventListener(Marker.MARKER_COPY, onMarkerCopy);
			viewRef.removeEventListener(Marker.MARKER_PASTE, onMarkerPaste);
		}
		
		/**
		 * Submits notifications of interest to PureMVC framework. 
		 * @return 
		 * 
		 */
		override public function listNotificationInterests():Array
		{
			var list:Array = super.listNotificationInterests();
			
			list.push(Notifications.PROJECT_IS_RESET);
			list.push(Notifications.OBJECT3D_SELECTED_MODEL);
			list.push(Notifications.OBJECT3D_POSITION_CHANGED_MODEL);
			list.push(Notifications.OBJECT3D_DELETED_MODEL);
			list.push(Notifications.TIMELINE_KEYFRAME_ADDED);
			list.push(Notifications.TIMELINE_KEYFRAME_REMOVED);
			list.push(Notifications.TIMELINE_KEYFRAME_UPDATED);
			list.push(Notifications.TIMELINE_HIDE_MARKER_ADDED);
			list.push(Notifications.TIMELINE_GLOBAL_MARKER_ADDED);
			list.push(Notifications.TIMELINE_GLOBAL_MARKER_REMOVED);
			list.push(Notifications.TIMELINE_EXTEND);
			list.push(Notifications.TIMELINE_AUTO_KEYFRAMING);
			
			
			
			return list;
		}
		
		/**
		 * Handles notifications. 
		 * @param notification
		 * 
		 */
		override public function handleNotification(notification:INotification):void
		{
			super.handleNotification(notification);
			
			switch(notification.getName())
			{
				case Notifications.PROJECT_IS_RESET:
				
					handleProjectIsReset();
					
					break;
						
				case Notifications.OBJECT3D_SELECTED_MODEL:
				
					handleObject3DSelected(notification.getBody() as String);
					
					break;
				
				case Notifications.OBJECT3D_POSITION_CHANGED_MODEL:
				
					handleObject3DPositionChanged(notification.getBody() as String);
					
					break;
					
				case Notifications.OBJECT3D_DELETED_MODEL:
				
					handleObject3DDeleted();
					
					break;
					
				case Notifications.TIMELINE_KEYFRAME_ADDED:
				
					handleKeyframeAdded(notification.getBody() as KeyframeVO);
					
					break;
					
				case Notifications.TIMELINE_KEYFRAME_REMOVED:
				
					handleKeyframeRemoved(notification.getBody() as Object3DVO);
					
					break;
					
				case Notifications.TIMELINE_KEYFRAME_UPDATED:
				
					handleKeyframeUpdated(notification.getBody() as KeyframeVO);
					
					break;
				
				case Notifications.TIMELINE_HIDE_MARKER_ADDED:
				
					handleHideMarkerAdded(notification.getBody() as HideMarkerVO);
					
					break;
					
				case Notifications.TIMELINE_GLOBAL_MARKER_ADDED:
				
					handleGlobalMarkerAdded(notification.getBody() as GlobalMarkerVO);
					
					break;
					
				case Notifications.TIMELINE_GLOBAL_MARKER_REMOVED:
				
					handleGlobalMarkerRemoved(notification.getBody() as String);
					
					break;
					
				case Notifications.TIMELINE_EXTEND:
				
					handleTimelineExtend();
					
					break;
				
				case Notifications.TIMELINE_AUTO_KEYFRAMING:
				
					handleAutoKeyframing(notification.getBody() as Boolean);
					
					break;
					
				
					
				
			}
		}
		
		/////////////////////////////////////////////////////
		//
		// PureMVC handler functions
		//
		/////////////////////////////////////////////////////
		
		/**
		 * Responds to the resetting of the project. 
		 * 
		 */
		protected function handleProjectIsReset():void
		{
			viewRef.thumbX = 0;
		}
		
		/**
		 * Responds to the user selecting an object3D. 
		 * @param name
		 * 
		 */
		protected function handleObject3DSelected(name:String):void
		{
			var VO:Object3DVO = settings.getObject3D(name);
			var keyframes:Dictionary = VO.keyframes;
			var hideMarkers:Dictionary = VO.hideMarkers;
			
			// remove all existing keyframes
			viewRef.removeMarkers(Keyframe);
			viewRef.removeMarkers(SplitMarker);
			
			for each(var kf:KeyframeVO in keyframes)
			{
				viewRef.addKeyframe(kf);
			}
			
			for each(var hm:HideMarkerVO in hideMarkers)
			{
				viewRef.addHideMarker(hm);
			}

		}
		
		/**
		 * Responds to the user deleting an object3D. 
		 * 
		 */
		protected function handleObject3DDeleted():void
		{
			viewRef.removeMarkers(Keyframe);
			viewRef.removeMarkers(SplitMarker);
		}
				
		/**
		 * Responds to the completion of a user moving an object3D.
		 * @param name
		 * 
		 */
		protected function handleObject3DPositionChanged(name:String):void
		{	
			// if no keyframe exists at this time interval, create one, otherwise update position
			var VO:Object3DVO = settings.getObject3D(name);
			var keyframes:Dictionary = VO.keyframes;
			var pos:Number3D;
			var kf:KeyframeVO;
			
			for each(kf in keyframes)
			{
				if(kf.time == _currentTimeInterval)
				{	
					pos = new Number3D(VO.x, VO.y, VO.z);
					
					var newKf:KeyframeVO = new KeyframeVO();
					newKf.id = kf.id;
					newKf.object3DName = kf.object3DName;
					newKf.position = pos;

					viewRef.updateKeyframePosition(kf.id, pos);
					
					sendNotification(Notifications.TIMELINE_SET_KEYFRAME_POSITION, newKf);
					
					sendNotification(Notifications.TIMELINE_CALCULATE_TWEENS, name);
					
					break;
				}
			}

			if(pos == null && _autoKeyframing)
			{
				kf = new KeyframeVO();
				kf.object3DName = VO.name;
				kf.position = new Number3D(VO.x, VO.y, VO.z);
				kf.time = _currentTimeInterval;
				
				// we need to create a new keyframe
				sendNotification(Notifications.TIMELINE_ADD_KEYFRAME, kf);
				
				scrubObjects3D();
			}
			
			 
		}
		
		/**
		 * Handles the case of the user requesting that the length of the timeline be extended. 
		 * 
		 */
		protected function handleTimelineExtend():void
		{
			var tWidth:uint = viewRef.width;
			
			viewRef.width = tWidth + (Timeline.SQUARE_WIDTH * viewRef.gridNumSpacing * 10);
			viewRef.tWidth = viewRef.width;
			viewRef.makeTimeline();
		}
		
		/**
		 * Handles the case of a keyframe being added to the timeline. 
		 * @param kf
		 * 
		 */
		protected function handleKeyframeAdded(kf:KeyframeVO):void
		{
			viewRef.addKeyframe(kf);

			sendNotification(Notifications.TIMELINE_CALCULATE_TWEENS, kf.object3DName);
			
			scrubObjects3D();
		}
		
		/**
		 * Handles the case of a keyframe being removed from the timeline. 
		 * @param VO
		 * 
		 */
		protected function handleKeyframeRemoved(VO:Object3DVO):void
		{
			sendNotification(Notifications.TIMELINE_CALCULATE_TWEENS, VO.name);
			
			scrubObjects3D();
		}
		
		/**
		 * Handles the case of a keyframe being changed in some way. 
		 * @param kf
		 * 
		 */
		protected function handleKeyframeUpdated(kf:KeyframeVO):void
		{
			sendNotification(Notifications.TIMELINE_CALCULATE_TWEENS, kf.object3DName);
			
			scrubObjects3D();
		}
		
		/**
		 * Responds to a hide marker being added to the model. 
		 * @param hm
		 * 
		 */
		protected function handleHideMarkerAdded(hm:HideMarkerVO):void
		{
			viewRef.addHideMarker(hm);
			
			scrubObjects3D();
		}
		
		/**
		 * Responds to a global marker being added to the model. 
		 * @param gm
		 * 
		 */
		protected function handleGlobalMarkerAdded(gm:GlobalMarkerVO):void
		{
			viewRef.addGlobalMarker(gm);
		}
		
		/**
		 * Responds to a global marker being removed from the model. 
		 * @param gm
		 * 
		 */
		protected function handleGlobalMarkerRemoved(id:String):void
		{
			if(id != null)
			{
				viewRef.removeMarker(id);
			}
		}
		
		protected function handleAutoKeyframing(value:Boolean):void
		{
			_autoKeyframing = value;
		}
		
		/////////////////////////////////////////////////////
		//
		// Generic object functions
		//
		/////////////////////////////////////////////////////
		
		/**
		 * Responds to a mouse up event on any SnapDraggableObject 
		 * @param evt
		 * 
		 */
		protected function onObjectUp(evt:Event):void
		{
			if(evt.target is Thumb)
			{
				thumbChange(evt);
			}
			
			if(evt.target is Keyframe || evt.target is GlobalMarker)
			{
				doMarkerCheck(evt);
			}
			
		}
		
		/**
		 * Responds to a user moving any SnapDraggableObject.
		 * @param evt
		 * 
		 */
		protected function onObjectMove(evt:Event):void
		{
			if(evt.target is Thumb)
			{
				thumbChange(evt);
			}
			
			if(evt.target is Keyframe)
			{
				keyframeChange(evt);
			}

			if(evt.target is SplitMarker || evt.target is SplitMarkerEnd)
			{
				hideMarkerChange(evt);
			}
			
			if(evt.target is GlobalMarker)
			{
				globalMarkerChange(evt);
			}
		}
		
		/**
		 * Deletes a single marker of the type specified by the target object.  
		 * @param evt
		 * 
		 */
		protected function onMarkerDelete(evt:Event):void
		{
			var marker:Marker;
			var VO:BaseMarker;
			
			if(evt.target is Keyframe)
			{
				marker = evt.target as Keyframe;
				VO = new KeyframeVO() as KeyframeVO;
				VO.object3DName = marker.object3DName;
				VO.id = marker.id;
				
				if(marker.time == 0)
				{
					return;
				}
				
				sendNotification(Notifications.TIMELINE_REMOVE_KEYFRAME, VO);
			}
			
			if(evt.target is SplitMarker)
			{
				marker = evt.target as SplitMarker;
				VO = new HideMarkerVO() as HideMarkerVO;
				
				VO.object3DName = marker.object3DName;
				VO.id = marker.id;
				
				sendNotification(Notifications.TIMELINE_REMOVE_HIDE_MARKER, VO);
			}
			
			scrubObjects3D();
			
			if(evt.target is GlobalMarker)
			{
				marker = evt.target as GlobalMarker;
				sendNotification(Notifications.TIMELINE_REMOVE_GLOBAL_MARKER, marker.id);
			}
			
			viewRef.removeMarker(marker.id);	
		}
		
		/**
		 * Deletes all markers of the type specified by the target object. 
		 * @param evt
		 * 
		 */
		protected function onMarkerDeleteAll(evt:Event):void
		{
			var marker:Marker;
			// var VO:MarkerVO;
			var lib:Dictionary;
			
			if(evt.target is Keyframe)
			{
				marker = evt.target as Keyframe;
				// VO = new KeyframeVO() as KeyframeVO;
				lib = (settings.getObject3D(marker.object3DName) as Object3DVO).keyframes;
			}
			
			if(evt.target is SplitMarker)
			{
				marker = evt.target as SplitMarker;
				// VO = new HideMarkerVO() as HideMarkerVO;
				lib = (settings.getObject3D(marker.object3DName) as Object3DVO).hideMarkers;
			}
			
			if(evt.target is GlobalMarker)
			{
				marker = evt.target as GlobalMarker;
				
				lib = settings.globalMarkers;
			}
			
			for each(var VO:BaseMarker in lib)
			{
				if(VO is KeyframeVO)
				{	
					if((VO as KeyframeVO).time != 0)
					{
						viewRef.removeMarker(VO.id);
						sendNotification(Notifications.TIMELINE_REMOVE_KEYFRAME, VO);
					}
					
				}
				
				if(VO is HideMarkerVO)
				{
					viewRef.removeMarker(VO.id);
					sendNotification(Notifications.TIMELINE_REMOVE_HIDE_MARKER, VO);
				}
				
				if(VO is GlobalMarkerVO)
				{
					viewRef.removeMarker(VO.id);
					sendNotification(Notifications.TIMELINE_REMOVE_GLOBAL_MARKER, VO.id);
					
				}
			}
			
			scrubObjects3D();
		}
		
		/**
		 * Carries out any necessary checks once a keyframe action is complete,
		 * as indicated by mouse up event. 
		 * 
		 */
		protected function doMarkerCheck(evt:Event):void
		{
			// check to see if the marker is lying on top of another marker
			var marker:Marker;
			var lib:Dictionary;
			
			if(evt.target is Keyframe)
			{
				marker = evt.target as Keyframe;
				lib = (settings.getObject3D(marker.object3DName) as Object3DVO).keyframes;

			}
			
			if(evt.target is GlobalMarker)
			{
				marker = evt.target as GlobalMarker;
				lib = settings.globalMarkers;
			}
			
			for each(var VO:BaseMarker in lib)
			{
				// prevent check being done to itself
				if(VO.id == marker.id)
				{
					continue;
				}
				
				// handle keyframes
				if(VO is KeyframeVO)
				{
					if((VO as KeyframeVO).time == marker.time)
					{
						viewRef.removeMarker(marker.id);
						VO = lib[marker.id];
						
						sendNotification(Notifications.TIMELINE_REMOVE_KEYFRAME, VO);
						
						scrubObjects3D();
						
						return;
					}
				}
				
				// handle global markers
				if(VO is GlobalMarkerVO)
				{
					if((VO as GlobalMarkerVO).time == marker.time)
					{
						viewRef.removeMarker(marker.id);
						
						sendNotification(Notifications.TIMELINE_REMOVE_GLOBAL_MARKER, marker.id);
						
						scrubObjects3D();
						
						return;
					}
				}
			}
		}
		
		protected function onMarkerCopy(evt:Event):void
		{
			
		}
		
		protected function onMarkerPaste(evt:Event):void
		{
			
		}
				
		/////////////////////////////////////////////////////
		//
		// Thumb functions
		//
		/////////////////////////////////////////////////////
		
		/**
		 * Responds to whenever the user invokes a mouse up event on the Thumb. 
		 * @param evt
		 * 
		 */
		protected function thumbChange(evt:Event):void
		{
			_currentTimeInterval = (evt.target as SnapDraggableObject).time;
			sendNotification(Notifications.TIMELINE_SET_CURRENT_TIME_INTERVAL, _currentTimeInterval);
			scrubObjects3D();
		}
		
		/////////////////////////////////////////////////////
		//
		// Keyframe functions
		//
		/////////////////////////////////////////////////////
		
		/**
		 * Responds to a keyframe being moved or a mouse up event. 
		 * @param evt
		 * 
		 */
		protected function keyframeChange(evt:Event):void
		{
			sendNotification(Notifications.TIMELINE_KEYFRAME_MOVED, evt.target as Keyframe);
		}
		
		
		
		/////////////////////////////////////////////////////
		//
		// Hide marker functions
		//
		/////////////////////////////////////////////////////
		
		/**
		 * Responds to a hide marker being moved or a mouse up event. 
		 * @param evt
		 * 
		 */
		protected function hideMarkerChange(evt:Event):void
		{
			var hideMarker:SplitMarker;
			
			if(evt.target is SplitMarkerEnd)
			{
				hideMarker = evt.target.parent as SplitMarker;
			}else
			{
				hideMarker = evt.target as SplitMarker;
			}
			
			viewRef.setChildIndex(hideMarker, viewRef.numChildren -1);	
			
			sendNotification(Notifications.TIMELINE_HIDE_MARKER_MOVED, hideMarker);

			scrubObjects3D();	
		}
		
		/////////////////////////////////////////////////////
		//
		// Global marker functions
		//
		/////////////////////////////////////////////////////
		
		/**
		 * Responds to a global marker being moved. 
		 * @param evt
		 * 
		 */
		protected function globalMarkerChange(evt:Event):void
		{	
			var globalMarker:GlobalMarker = evt.target as GlobalMarker;
			
			sendNotification(Notifications.TIMELINE_GLOBAL_MARKER_MOVED, globalMarker);
		}
		
		/////////////////////////////////////////////////////
		//
		// Utility functions
		//
		/////////////////////////////////////////////////////
		
		/**
		 * Resets the position and hide/show status of an object3D, according to the thumb position. 
		 * 
		 */
		protected function scrubObjects3D():void
		{	
			var currentAutoKeyFraming:Boolean = _autoKeyframing;
			_autoKeyframing = false;

			sendNotification(Notifications.TIMELINE_SCRUB_OBJECTS3D, _currentTimeInterval);

			_autoKeyframing = currentAutoKeyFraming;
		}
		
		/**
		 * Returns typed reference to the model. 
		 * @return 
		 * 
		 */
		protected function get settings():ProjectSettingsProxy
		{
			var proxy:IProxy = facade.retrieveProxy(ProjectSettingsProxy.NAME);
			
			return proxy as ProjectSettingsProxy;
		}
		
		/**
		 * Returns typed reference to component. 
		 * @return 
		 * 
		 */
		protected function get viewRef():TimelineGrid
		{
			return viewComponent as TimelineGrid;
		}
	}
}